﻿using HardcoreGame.Content.Items.Consumables.Fishes;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Terraria;
using Terraria.DataStructures;
using Terraria.GameContent;
using Terraria.ID;
using Terraria.ModLoader;
using Terraria.ModLoader.IO;

namespace HardcoreGame.Content.Tiles.Machines
{
    public class DryingRackTE : ModTileEntity
    {
        public static int ValidTileID
        {
            get
            {
                return ModContent.TileType<DryingRack>();
            }
        }
        public float[] waterContent = new float[3];
        public int[] startDryingTime = new int[3];

        public Item[] items = [new Item(), new Item(), new Item()];
        private static readonly int[] ValidItemIDs =
        [
            ItemID.ArmoredCavefish,
            ItemID.AtlanticCod,
            ItemID.Bass,
            ItemID.ChaosFish,
            ItemID.CrimsonTigerfish,
            ItemID.Damselfish,
            ItemID.DoubleCod,
            ItemID.Ebonkoi,
            ItemID.FlarefinKoi,
            ItemID.Flounder,
            ItemID.FrostMinnow,
            ItemID.GoldenCarp,
            ItemID.Hemopiranha,
            ItemID.NeonTetra,
            ItemID.Obsidifish,
            ItemID.PrincessFish,
            ItemID.Prismite,
            ItemID.RedSnapper,
            ItemID.Salmon,
            ItemID.SpecularFish,
            ItemID.Stinkfish,
            ItemID.Trout,
            ItemID.Tuna,
            ItemID.VariegatedLardfish
        ];
        private static readonly Dictionary<int, int> DriedItemMap = new()
        {
            { ItemID.ArmoredCavefish, ModContent.ItemType<ArmoredCavefish>() },
            { ItemID.AtlanticCod, ModContent.ItemType<AtlanticCod>() },
            { ItemID.Bass, ModContent.ItemType<Bass>() },
            { ItemID.ChaosFish, ModContent.ItemType<ChaosFish>()},
            { ItemID.CrimsonTigerfish, ModContent.ItemType<CrimsonTigerfish>() },
            { ItemID.Damselfish, ModContent.ItemType<Damselfish>() },
            { ItemID.DoubleCod, ModContent.ItemType<DoubleCod>() },
            { ItemID.Ebonkoi, ModContent.ItemType<Ebonkoi>() },
            { ItemID.FlarefinKoi, ModContent.ItemType<FlarefinKoi>() },
            { ItemID.Flounder, ModContent.ItemType<Flounder>() },
            { ItemID.FrostMinnow, ModContent.ItemType<FrostMinnow>() },
            { ItemID.GoldenCarp, ModContent.ItemType<GoldenCarp>() },
            { ItemID.Hemopiranha, ModContent.ItemType<Hemopiranha>() },
            { ItemID.NeonTetra, ModContent.ItemType<NeonTetra>() },
            { ItemID.Obsidifish, ModContent.ItemType<Obsidifish>() },
            { ItemID.PrincessFish, ModContent.ItemType<PrincessFish>() },
            { ItemID.Prismite, ModContent.ItemType<Prismite>() },
            { ItemID.RedSnapper, ModContent.ItemType<RedSnapper>() },
            { ItemID.Salmon, ModContent.ItemType<Salmon>() },
            { ItemID.SpecularFish, ModContent.ItemType<SpecularFish>() },
            { ItemID.Stinkfish, ModContent.ItemType<Stinkfish>() },
            { ItemID.Trout, ModContent.ItemType<Trout>() },
            { ItemID.Tuna, ModContent.ItemType<Tuna>() },
            { ItemID.VariegatedLardfish, ModContent.ItemType<VariegatedLardfish>() }
        };
        public static bool CanPlaceItem(Item item)
        {
            if (item == null || item.IsAir)
                return false;
            for (int i = 0; i < ValidItemIDs.Length; i++)
            {
                if (item.type == ValidItemIDs[i])
                    return true;
            }

            return false;
        }
        public bool IsItemDrying(int slot)
        {
            if (slot >= 0 && slot < items.Length && !items[slot].IsAir)
            {
                return ValidItemIDs.Contains(items[slot].type) && waterContent[slot] > 0;
            }

            return false;
        }
        public float GetDryProgress(int slot)
        {
            if (IsItemDrying(slot))
            {
                return 100f - waterContent[slot];
            }

            return 100f;
        }
        public override void SaveData(TagCompound tag)
        {
            for (int i = 0; i < 3; i++)
            {
                tag.Add($"WaterContent{i}", waterContent[i]);
                tag.Add($"StartDryingTime{i}", startDryingTime[i]);
                tag.Add($"Item{i}", items[i] ?? AirItem());
            }
        }

        public override void LoadData(TagCompound tag)
        {
            for (int i = 0; i < 3; i++)
            {
                waterContent[i] = tag.GetFloat($"WaterContent{i}");
                startDryingTime[i] = tag.GetInt($"StartDryingTime{i}");
                items[i] = ItemIO.Load(tag.GetCompound($"Item{i}"));
            }
        }

        public override void NetSend(BinaryWriter writer)
        {
            for (int i = 0; i < 3; i++)
            {
                writer.Write(waterContent[i]);
                writer.Write(startDryingTime[i]);
                ItemIO.Send(items[i], writer, true, false);
            }
        }

        public override void NetReceive(BinaryReader reader)
        {
            for (int i = 0; i < 3; i++)
            {
                waterContent[i] = reader.ReadSingle();
                startDryingTime[i] = reader.ReadInt32();
                ItemIO.Receive(items[i], reader, true, false);
            }
        }

        public override bool IsTileValidForEntity(int i, int j)
        {
            Tile tile = Framing.GetTileSafely(i, j);
            return tile.TileType == ValidTileID && tile.HasTile && tile.TileFrameX % 108 == 0 && tile.TileFrameY % 54 == 0;
        }

        public override int Hook_AfterPlacement(int i, int j, int type, int style, int direction, int alternate)
        {
            i--;
            j -= 2;
            if (Main.netMode == NetmodeID.MultiplayerClient)
            {
                NetMessage.SendTileSquare(Main.myPlayer, i, j, 3, 3, 0);
                NetMessage.SendData(MessageID.TileEntityPlacement, -1, -1, null, i, j, Type, 0f, 0, 0, 0);
                return -1;
            }
            return Place(i, j);
        }

        public override void OnNetPlace()
        {
            NetMessage.SendData(MessageID.TileEntitySharing, -1, -1, null, ID, Position.X, Position.Y, 0f, 0, 0, 0);
        }

        public override void Update()
        {
            for (int i = 0; i < 3; i++)
            {
                if (!items[i].IsAir && CanPlaceItem(items[i]))
                {
                    if (waterContent[i] <= 0)
                    {
                        waterContent[i] = 100f;
                        startDryingTime[i] = (int)Main.GameUpdateCount;
                    }
                    float dryRate = 0.01f;
                    if (Main.raining)
                    {
                        dryRate = -0.005f;
                    }
                    else if (Main.dayTime)
                    {
                        dryRate *= 2f;
                    }
                    waterContent[i] = Math.Max(0f, waterContent[i] - dryRate);
                    if (waterContent[i] <= 0)
                    {
                        if (DriedItemMap.TryGetValue(items[i].type, out int driedItemType))
                        {
                            Item driedItem = new();
                            driedItem.SetDefaults(driedItemType);
                            driedItem.stack = items[i].stack;
                            items[i] = driedItem;
                        }
                        waterContent[i] = 0f;
                    }
                }
            }
        }

        public override void OnKill()
        {
            for (int i = 0; i < 3; i++)
            {
                if (!items[i].IsAir)
                {
                    DropItem(i);
                }
            }
        }

        public override string ToString()
        {
            return $"DryingRackTE at {Position.X}x {Position.Y}y with items: {items[0]}, {items[1]}, {items[2]}";
        }

        public void DropItem(int slot)
        {
            if (Main.netMode != NetmodeID.MultiplayerClient && !items[slot].IsAir)
            {
                Item.NewItem(new EntitySource_TileBreak(Position.X, Position.Y, null), Position.X * 16, Position.Y * 16, 48, 48, items[slot].netID, 1, false, items[slot].prefix, false, false);
                items[slot] = AirItem();
            }
        }

        public static bool CheckValidItem(Item i)
        {
            return !i.IsAir && CanPlaceItem(i);
        }

        public static void TryPlacing(int x, int y, int slot, int netid, int prefix, int stack)
        {

            WorldGen.RangeFrame(x, y, x + 3, y + 3);
            DryingRackTE te = ModContent.GetInstance<DryingRackTE>();
            int index = te.Find(x, y);

            if (index == -1)
            {
                int num2 = Item.NewItem(new EntitySource_TileBreak(x, y, null), x * 16, y * 16, 48, 48, 1, 1, false, 0, false, false);
                Main.item[num2].netDefaults(netid);
                Main.item[num2].Prefix(prefix);
                Main.item[num2].stack = stack;
                NetMessage.SendData(MessageID.SyncItem, -1, -1, null, num2, 0f, 0f, 0f, 0, 0, 0);
                return;
            }

            te = (DryingRackTE)ByID[index];
            if (te.items[slot].stack > 0)
            {
                te.DropItem(slot);
            }

            te.items[slot] = new Item();
            te.items[slot].netDefaults(netid);
            te.items[slot].Prefix(prefix);
            te.items[slot].stack = stack;

            NetMessage.SendData(MessageID.TileEntitySharing, -1, -1, null, te.ID, x, y, 0f, 0, 0, 0);
        }

        public static void OnPlayerInteraction(Player player, int clickX, int clickY)
        {
            Tile tile = Main.tile[clickX, clickY];
            int frameX = tile.TileFrameX % 108;
            int relativeX = clickX * 16 - (clickX - frameX / 18) * 16;
            int slot = relativeX < 38 ? 0 : relativeX < 76 ? 1 : 2;

            Item selectedItem = player.inventory[player.selectedItem];

            if (CheckValidItem(selectedItem) && !selectedItem.favorited)
            {
                player.GamepadEnableGrappleCooldown();
                PlaceItemOnBase(player, clickX, clickY, slot);
                Recipe.FindRecipes(false);
                return;
            }

            int x = clickX - Main.tile[clickX, clickY].TileFrameX % 108 / 18;
            int y = clickY - Main.tile[x, clickY].TileFrameY % 54 / 18;
            int index = ModContent.GetInstance<DryingRackTE>().Find(x, y);

            if (index != -1 && ((DryingRackTE)ByID[index]).items[slot].stack > 0)
            {
                player.GamepadEnableGrappleCooldown();
                ((DryingRackTE)ByID[index]).DropItem(slot);
                if (Main.netMode == NetmodeID.MultiplayerClient)
                {
                    NetMessage.SendData(MessageID.TileEntitySharing, -1, -1, null, index, x, y, 0f, 0, 0, 0);
                }
            }
        }

        private static void PlaceItemOnBase(Player player, int x, int y, int slot)
        {
            if (!player.ItemTimeIsZero)
            {
                return;
            }

            x -= Main.tile[x, y].TileFrameX % 108 / 18;
            y -= Main.tile[x, y].TileFrameY % 54 / 18;
            int index = ModContent.GetInstance<DryingRackTE>().Find(x, y);

            if (index == -1)
            {
                return;
            }

            if (((DryingRackTE)ByID[index]).items[slot].stack > 0)
            {
                ((DryingRackTE)ByID[index]).DropItem(slot);
            }

            if (Main.netMode == NetmodeID.MultiplayerClient)
            {
                DisplayBaseNet.DisplayBaseTryPlace(x, y, slot, player.selectedItem, player.whoAmI, 1);
            }
            else
            {
                TryPlacing(x, y, slot, player.inventory[player.selectedItem].netID, player.inventory[player.selectedItem].prefix, 1);
            }

            player.inventory[player.selectedItem].stack--;
            if (player.inventory[player.selectedItem].stack <= 0)
            {
                player.inventory[player.selectedItem].SetDefaults(0);
                Main.mouseItem.SetDefaults(0);
            }

            if (player.selectedItem == 58)
            {
                Main.mouseItem = player.inventory[player.selectedItem].Clone();
            }

            player.releaseUseItem = false;
            player.mouseInterface = true;
            player.PlayDroppedItemAnimation(20);
            WorldGen.RangeFrame(x, y, x + 3, y + 3);
        }

        public static void GetItemDrawFrame(int item, out Texture2D itemTexture, out Rectangle itemFrame)
        {
            Main.instance.LoadItem(item);
            itemTexture = TextureAssets.Item[item].Value;
            if (Main.itemAnimations[item] != null)
            {
                itemFrame = Main.itemAnimations[item].GetFrame(itemTexture, -1);
                return;
            }
            itemFrame = itemTexture.Frame(1, 1, 0, 0, 0, 0);
        }

        public static Item AirItem()
        {
            Item item = new();
            item.TurnToAir(false);
            return item;
        }
    }
}